#include <math.h>
#include <stdlib.h>
#include <string.h>
#include "position.h"

#define POSITION_GET_PRIVATE(obj)	(G_TYPE_INSTANCE_GET_PRIVATE((obj),	TYPE_POSITION, PositionPrivate))
G_DEFINE_TYPE (Position, position, G_TYPE_OBJECT);

typedef struct _PositionPrivate PositionPrivate;
struct _PositionPrivate {
		
	gint map[8][8];
	
	gint double_push; 
	gint on_move;
	gint half_moves;
	gint game_num;
	gboolean king_castle[2];
	gboolean queen_castle[2];
};


static gboolean position_is_legal_move 			(Position *pos, gint from_x, gint from_y, gint to_x, gint to_y);
static gboolean position_is_legal_pawn_move		(Position *pos, gint from_x, gint from_y, gint to_x, gint to_y);
static gboolean position_is_legal_knight_move	(Position *pos, gint from_x, gint from_y, gint to_x, gint to_y);
static gboolean position_is_legal_bishop_move	(Position *pos, gint from_x, gint from_y, gint to_x, gint to_y);
static gboolean position_is_legal_rook_move		(Position *pos, gint from_x, gint from_y, gint to_x, gint to_y);
static gboolean position_is_legal_king_move		(Position *pos, gint from_x, gint from_y, gint to_x, gint to_y);
static gboolean position_is_legal_queen_move	(Position *pos, gint from_x, gint from_y, gint to_x, gint to_y);
static gboolean king_in_check 					(Position *pos, int color);

char piece_to_char[12] = {'P', 'N', 'B', 'R', 'K', 'Q', 'p', 'n', 'b', 'r', 'k', 'q'};
static void
position_class_init (PositionClass *klass)
{
	GObjectClass *object_class;
	g_type_class_add_private (klass, sizeof (PositionPrivate));
	object_class = (GObjectClass *)klass;
}

static void
position_init (Position *pos)
{
	PositionPrivate *priv = POSITION_GET_PRIVATE (pos);
	gint rank, file;
	
	for (file = 0; file < 8; file ++)
		for (rank = 0; rank < 8; rank++)
			priv->map[file][rank]=NOPIECE;
			
	for (file = 0; file < 8; file++) {
		priv->map[file][1] = W_PAWN;
		priv->map[file][6] = B_PAWN;
	}

	priv->map[0][0] = W_ROOK;
	priv->map[1][0] = W_KNIGHT;
	priv->map[2][0] = W_BISHOP;
	priv->map[3][0] = W_QUEEN;
	priv->map[4][0] = W_KING;
	priv->map[5][0] = W_BISHOP;
	priv->map[6][0] = W_KNIGHT;
	priv->map[7][0] = W_ROOK;
	priv->map[0][7] = B_ROOK;
	priv->map[1][7] = B_KNIGHT;
	priv->map[2][7] = B_BISHOP;
	priv->map[3][7] = B_QUEEN;
	priv->map[4][7] = B_KING;
	priv->map[5][7] = B_BISHOP;
	priv->map[6][7] = B_KNIGHT;
	priv->map[7][7] = B_ROOK;

	
	priv->double_push = -1;
	priv->on_move = 0;
	priv->half_moves = 0;
	priv->king_castle[0]=TRUE;
	priv->king_castle[1]=TRUE;
	priv->queen_castle[0]=TRUE;
	priv->queen_castle[1]=TRUE;
}

Position *
position_new (void)
{
	return g_object_new (TYPE_POSITION, NULL);
}

static void
position_get_king_coords (Position *pos, gint *file, gint *rank, gint color)
{
	PositionPrivate *priv = POSITION_GET_PRIVATE (pos);
	gint f, r;
	
	for (f = 0; f < 8; f++)
		for (r = 0; r < 8; r++)
			if (color == 1 && priv->map[f][r] == B_KING) {
				*file = f;
				*rank = r;
				return;
			} else if (color == 0 && priv->map[f][r] == W_KING) {
				*file = f;
				*rank = r;
				return;
			} 
	
}

static gboolean
king_in_check (Position *pos, gint color)
{
	PositionPrivate *priv;
	
	gint file, rank;
	gint k_file, k_rank;
	priv = POSITION_GET_PRIVATE (pos);
	
	position_get_king_coords (pos, &k_file, &k_rank, color);
	
	priv->map[k_file][k_rank] = NOPIECE;
	
	for (file = 0; file < 8; file++) {
		for (rank = 0; rank < 8; rank++) {
			if ((priv->map[file][rank] % 6) == KING)
				continue;
			
			if (position_is_legal_move (pos, file, rank, k_file, k_rank)) {
				if (color)
					priv->map[k_file][k_rank] = B_KING;
				else
					priv->map[k_file][k_rank] = W_KING;
				
				return TRUE;
			}
		}
	}
	
	if (color)
		priv->map[k_file][k_rank] = B_KING;
	else
		priv->map[k_file][k_rank] = W_KING;
	
	return FALSE;
}	

void
position_print_fen (Position *pos)
{
	//gchar *fen_pos;
	PositionPrivate *priv;
	gint file, rank, count = 0, count2 = 0;
	gchar fen_string[74];
	
	priv = POSITION_GET_PRIVATE (pos);
	
	for (rank = 7; rank >= 0; rank--) {
		count = 0;
		for (file = 0; file < 8; file++) {
			if (priv->map[file][rank] != NOPIECE) {
				if (count) {
					fen_string[count2++] = count +'0';
					count = 0;
				}
				fen_string[count2++] = piece_to_char [priv->map[file][rank]];
			} else {
				if (file == 7) {
					fen_string[count2++] = count + '0' + 1;
				} else {
					count++;
				}
			}
		}
		fen_string[count2++] = '/';
	}
	fen_string[--count2] = '\0';
	/*fen_string[--count2] = ' ';
	fen_string[count2++] = ' ';
	if (priv->on_move)
		fen_string[count2++] = 'b';
	else
		fen_string[count2++] = 'w';
	
	fen_string[count2++] = ' ';*/
	
	/*if (!priv->king_castle[0] && !priv->king_castle[1] && !priv->queen_castle[0] && !priv->queen_castle[0])
		fen_string[count2++] = '-';	
	
	if (priv->king_castle[0])
		fen_string[count2++] = 'K';
	if (priv->queen_castle[0])
		fen_string[count2++] = 'Q';
	if (priv->king_castle[1])
		fen_string[count2++] = 'k';
	if (priv->queen_castle[1])
		fen_string[count2++] = 'q';
		
	fen_string[count2++] = ' ';*/
	
	/*if (priv->double_push != -1) {
		fen_string[count2++] = priv->double_push + 'a';
		
		if (priv->on_move)
			fen_string[count2++] = '3';
		else	
			fen_string[count2++] = '6';
	} else {
		fen_string[count2++] = '-';	
	}
	
	
	fen_string[count2++] = ' ';
	fen_string[count2++] = '0';
	fen_string[count2++] = ' ';
	fen_string[count2++] = priv->half_moves;
	fen_string[count2++] = '\n';*/
	
	
		
	g_print ("%s\n", fen_string);
}



void
position_print (Position *pos)
{
	PositionPrivate *priv;
	gint file, rank;
	
	priv = POSITION_GET_PRIVATE (pos);
	for (rank = 7; rank >=0 ; rank--) {
	for (file = 0; file < 8; file++) {
		
			switch (priv->map[file][rank]) {
				case W_ROOK:
				g_print ("R");
				break;
			case W_KNIGHT:
				g_print ("N");
				break;
			case W_BISHOP:
				g_print ("B");
				break;
			case W_QUEEN:
				g_print ("Q");
				break;
			case W_KING:
				g_print ("K");
				break;
			case W_PAWN:
				g_print ("P");
				break;
			case B_ROOK:
				g_print ("r");
				break;
			case B_KNIGHT:
				g_print ("n");
				break;
			case B_BISHOP:
				g_print ("b");
				break;
			case B_QUEEN:
				g_print ("q");
				break;
			case B_KING:
				g_print ("k");
				break;
			case B_PAWN:
				g_print ("p");
				break;
			case NOPIECE:
				g_print ("-");
				break;
			}
		}
		g_print ("\n");	
	}
}

void
position_do_move (Position *pos, Move *mov)
{
	PositionPrivate *priv;
	priv = POSITION_GET_PRIVATE (pos);
	
	gint file, rank;
	gint temp;
	gboolean legal = FALSE;
	priv->half_moves++;
	if (mov->type == M_QCASTLE && priv->on_move) {
		priv->map[2][7] = B_KING;
		priv->map[3][7] = B_ROOK;
		priv->map[0][7] = NOPIECE;
		priv->map[4][7] = NOPIECE;
		priv->double_push = -1;
	} else if (mov->type == M_QCASTLE && !priv->on_move) {
		priv->map[2][0] = W_KING;
		priv->map[3][0] = W_ROOK;
		priv->map[0][0] = NOPIECE;
		priv->map[4][0] = NOPIECE;
		priv->double_push = -1;
	} else if (mov->type == M_KCASTLE && priv->on_move) {
		priv->map[6][7] = B_KING;
		priv->map[5][7] = B_ROOK;
		priv->map[7][7] = NOPIECE;
		priv->map[4][7] = NOPIECE;
		priv->double_push = -1;
	} else if (mov->type == M_KCASTLE && !priv->on_move) {
		priv->map[6][0] = W_KING;
		priv->map[5][0] = W_ROOK;
		priv->map[7][0] = NOPIECE;
		priv->map[4][0] = NOPIECE;
		priv->double_push = -1;
	} else if (mov->type == M_DROP) {
		if (priv->on_move)
			mov->piece += 6;
		
		priv->map[mov->to_file][mov->to_rank] = mov->piece;
		priv->double_push = -1;
	} else if (mov->type == M_PROMOTE) {
		priv->double_push = -1;
			
		if (!priv->on_move) {
			priv->map[mov->from_file][6] = NOPIECE;
			priv->map[mov->to_file][7] = mov->promote_to;
		} else {
			priv->map[mov->from_file][1] = NOPIECE;
			priv->map[mov->to_file][0] = mov->promote_to + 6;
		}
		
	} else if (mov->type == M_REGULAR || mov->type == M_CAPTURE) {
		
		if (priv->on_move && mov->piece != -1)
			mov->piece+=6;
		//g_debug ("regular move, piece = %i", mov->piece);
		if (mov->piece == W_KING) {
			position_get_king_coords (pos, &file, &rank, 0);
			priv->map[file][rank] = NOPIECE;
			priv->map[mov->to_file][mov->to_rank] = W_KING;
			priv->double_push = -1;
			/*priv->king_castle[0]=FALSE;
			priv->queen_castle[0]=FALSE;*/
			return;
		} else if (mov->piece == B_KING) {
			position_get_king_coords (pos, &file, &rank, 1);
			priv->map[file][rank] = NOPIECE;
			priv->map[mov->to_file][mov->to_rank] = B_KING;
			priv->double_push = -1;
			/*priv->king_castle[1]=FALSE;
			priv->queen_castle[1]=FALSE;*/
			return;
		}
			
		
		for (file = 0; file < 8; file++) {
			for (rank = 0; rank < 8; rank++) {
				if (priv->map[file][rank] == mov->piece) {
					//g_debug ("piece located on %i %i", file, rank);
					if (mov->from_rank != -1 && rank != mov->from_rank)
						continue;
					if (mov->from_file != -1 && file != mov->from_file)
						continue;
					legal = position_is_legal_move (pos, file, rank, mov->to_file, mov->to_rank);
			
					if (legal) {
						temp = priv->map[mov->to_file][mov->to_rank];
						priv->map[file][rank] = NOPIECE;
						
						priv->map[mov->to_file][mov->to_rank] = mov->piece;
						
						if (priv->on_move)
							priv->on_move = 0;
						else
							priv->on_move = 1;
						
						if (king_in_check(pos, !priv->on_move)) {
							
							priv->map[file][rank] = mov->piece;
							priv->map[file][rank] = temp;
							
							if (priv->on_move)
								priv->on_move = 0;
							else
								priv->on_move = 1;
							
						} else {
														
							
							if (file == mov->to_file && abs (mov->to_rank - rank) == 2 && (mov->piece%6) == PAWN)
								priv->double_push = file;
							else
								priv->double_push = -1;
													
							return;
						}
						
						
					}
		
				}
			}
		}
	}
	
	if (priv->on_move)
		priv->on_move = 0;
	else
		priv->on_move = 1;
	
}


	

static gboolean
position_is_legal_move (Position *pos, gint from_x, gint from_y, gint to_x, gint to_y)
{
	PositionPrivate *priv;
	gint handle = NOPIECE;
	
	priv = POSITION_GET_PRIVATE (pos);

	
	handle = priv->map[from_x][from_y];
	if (handle == NOPIECE)
		return FALSE;

	/* Wrong color */
	if (handle > 5 && !priv->on_move)
		return FALSE;

	if (handle < 6 && priv->on_move)
		return FALSE;

	/* Can't capture own pieces */
	if (priv->map[to_x][to_y] != NOPIECE) {
		if (priv->map[to_x][to_y] > 5 && priv->on_move)
			return FALSE;
		if (priv->map[to_x][to_y] < 6 && !priv->on_move)
			return FALSE;
	}

	/* Same square */
	if ((from_y == to_y) && (from_x == to_x))
		return FALSE;

	switch (handle % 6)	{
		case 0: //pawn
			return position_is_legal_pawn_move (pos, from_x, from_y, to_x, to_y);
			break;
		case 1: //knight
			return position_is_legal_knight_move (pos, from_x, from_y, to_x, to_y);
			break;
		case 2: //bishop
			return position_is_legal_bishop_move (pos, from_x, from_y, to_x, to_y);
			break;
		case 3: //rook
			return position_is_legal_rook_move (pos, from_x, from_y, to_x, to_y);
			break;
		case 4: // king
			return position_is_legal_king_move (pos, from_x, from_y, to_x, to_y);
			break;
		case 5: // queen
			return position_is_legal_queen_move (pos, from_x, from_y, to_x, to_y);
			break;
		default:
			return FALSE;
			break;
	}

	return FALSE;
}

static gboolean
position_is_legal_pawn_move (Position *pos, gint from_x, gint from_y, gint to_x, gint to_y)
{
	PositionPrivate *priv = POSITION_GET_PRIVATE (pos);
	//g_debug ("is legal pawn move %i %i %i %i", from_x, from_y, to_x, to_y);
	if (from_x == to_x) {/*e4e5 */
		if (priv->map[to_x][to_y] != NOPIECE) 
			return FALSE;

		if (!priv->on_move) { // white's move
			if (to_y - from_y == 1) 
				return TRUE;
			if ((from_y == 1) && (to_y - from_y == 2) && priv->map[from_x][2] == NOPIECE) 
				return TRUE;
		} else {
			if (from_y - to_y == 1) 
				return TRUE;
			if ((from_y == 6) && (from_y - to_y == 2) && priv->map[from_x][5] == NOPIECE) 
				return TRUE;
		}
		return FALSE;
	} if (from_x != to_x) { /*e4d5, e5d6 (enpassant)*/
		if ((from_x - to_x != 1) && (to_x - from_x != 1)) 
			return FALSE;
		if ((from_y - to_y != 1) && (to_y - from_y != 1)) 
			return FALSE;

		if (!priv->on_move) {
			if (from_y > to_y) 
				return FALSE;
			if ((priv->map[to_x][to_y] != NOPIECE) && priv->map[to_x][to_y] > 5)
				return TRUE;
			
			if (priv->double_push == to_x)
				return TRUE; // enpassant.
		} else {
			if (to_y > from_y) 
				return FALSE;
			if ((priv->map[to_x][to_y] != NOPIECE) && priv->map[to_x][to_y] < 6)
				return TRUE;

			if (priv->double_push == to_x)
				return TRUE; // enpassant 
		}
	}
	return FALSE;
}

static gboolean
position_is_legal_knight_move (Position *pos, gint from_x, gint from_y, gint to_x, gint to_y)
{
	gint dx, dy;

	dx = from_x - to_x;
	dy = from_y - to_y;
	if (abs (dx) == 2) 
	{
		if (abs (dy) == 1)
			return TRUE;
	}
	if (abs (dy) == 2) 
	{
		if (abs (dx) == 1)
			return TRUE;
	}
	return FALSE;
}

static gboolean
position_is_legal_bishop_move (Position *pos, gint from_x, gint from_y, gint to_x, gint to_y)
{
	PositionPrivate *priv = POSITION_GET_PRIVATE (pos);
	gint dx, dy, x, y;
	gint startx, starty;
	gint count;
	gint incx, incy;

	if (from_x > to_x) {
		dx = from_x - to_x;
		incx = -1;
	} else {
		dx = to_x - from_x;
		incx = 1;
	}
	
	startx = from_x + incx;
	
	if (from_y > to_y) {
		dy = from_y - to_y;
		incy = -1;
	} else {
		dy = to_y - from_y;
		incy = 1;
	}
	
	starty = from_y + incy;

	if (dx != dy)
		return FALSE;			

	if (dx == 1)
		return TRUE;
		
	count = dx - 1;
	
	for (x = startx, y = starty; count; x += incx, y += incy, count--) {
		if (priv->map[x][y] != NOPIECE)
			return FALSE;
	}
	return TRUE;
}

static gboolean
position_is_legal_rook_move (Position *pos, gint from_x, gint from_y, gint to_x, gint to_y)
{
	PositionPrivate *priv = POSITION_GET_PRIVATE (pos);
	int i;
	int start, stop;
	
	if (from_x == to_x) 
	{
		if (abs(from_y - to_y) == 1)
			return 1;
		if (from_y < to_y) {
			start = from_y + 1;
			stop = to_y - 1;
		} else {
			start = to_y + 1;
			stop = from_y - 1;
		}
		
		for (i = start; i <= stop; i++) {
			if (priv->map[from_x][i] != NOPIECE)
				return FALSE;
		}
		return TRUE;
	} else if (from_y == to_y) {
		if (abs (from_x - to_x) == 1)
			return TRUE;
		if (from_x < to_x) {
			start = from_x + 1;
			stop = to_x - 1;
		} else {
			start = to_x + 1;
			stop = from_x - 1;
		}
		
		for (i = start; i <= stop; i++) {
			if (priv->map[i][from_y] != NOPIECE)
				return FALSE;
		}
		return TRUE;
	}
	return FALSE;
}

static gboolean
position_is_legal_queen_move (Position *pos, gint from_x, gint from_y, gint to_x, gint to_y)
{
	return position_is_legal_rook_move (pos, from_x, from_y, to_x, to_y) ||
		   position_is_legal_bishop_move (pos, from_x, from_y, to_x, to_y);
}

static gboolean
position_is_legal_king_move (Position *pos, gint from_x, gint from_y, gint to_x, gint to_y)
{
	//if (from_x == 4 && to_x == 6)
	//	return legal_castle(gs, from_x, from_y, to_x, to_y);
	//if (from_x == 4 && to_x == 2)
	//	return legal_castle(gs, from_x, from_y, to_x, to_y);
	//if ((from_x == FILE_QCASTLE && to_x == 2) || (from_x == FILE_KCASTLE && to_x == 6))
	//	return legal_castle(gs, from_x, from_y, to_x, to_y);
	if (abs(from_x - to_x) > 1)
		return FALSE;
	if (abs(from_y - to_y) > 1)
		return FALSE;
	return TRUE;
}

